#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <gmp.h>
#include <pthread.h>

pthread_mutex_t mutex_y_curr, mutex_y_next, mutex_a_curr;
pthread_t thread_1, thread_2, thread_3, thread_4, thread_5;
void *start_1();
void *start_2();
void *start_3();
void *start_4();
void *start_5();

/*Declaracao de Variaveis mpf_t para precisao float da biblioteca GMP*/
mpf_t y_curr;
mpf_t y_next;
mpf_t a_curr;
mpf_t a_next;
mpf_t y_temp;
mpf_t y_temp1;
mpf_t a_temp;
mpf_t a_temp1;
mpf_t pi;
long int temp;
int count;

void create_threads()
{
    //Threads para cálculo do y_next
    pthread_create(&thread_1,NULL,start_1,NULL);
    pthread_create(&thread_2,NULL,start_2,NULL);
    //Espera as threads terminarem 
    pthread_join(thread_1,NULL);
    pthread_join(thread_2,NULL);
    //Calcula y_next
    //y_next = y_temp1/y_temp
    mpf_div(y_next,y_temp1,y_temp);

    //Threads para cálculo do a_next
    pthread_create(&thread_3,NULL,start_3,NULL);
    pthread_create(&thread_4,NULL,start_4,NULL);
    pthread_create(&thread_5,NULL,start_5,NULL);
    //Espera as threads terminarem  
    pthread_join(thread_3,NULL);
    pthread_join(thread_4,NULL);
    pthread_join(thread_5,NULL);
    //Calcula a_next
    //a_next = a_temp1-temp*a_temp
    mpf_mul_ui(a_temp,a_temp,temp);
    mpf_sub(a_next,a_temp1,a_temp);

    //Calcula pi
    mpf_ui_div(pi,1,a_next);

    //Atualiza variaveis
    mpf_set(y_curr, y_next);
    mpf_set(a_curr, a_next);
}


/*
    y_next =(1 - pow((1-pow(y_curr, 4)),0.25))/(1 + pow((1-pow(y_curr, 4)),0.25));
*/
void *start_1()
{
    //y_temp = (1 + pow((1-pow(y_curr, 4)),0.25))
    mpf_pow_ui(y_temp,y_curr,4);
    mpf_ui_sub(y_temp,1,y_temp);
    mpf_sqrt(y_temp,y_temp);
    mpf_sqrt(y_temp,y_temp);
    mpf_add_ui(y_temp,y_temp, 1);
}
void *start_2()
{
    //y_curr = (1 - pow((1-pow(y_curr, 4)),0.25))
    mpf_pow_ui(y_temp1,y_curr,4);
    mpf_ui_sub(y_temp1,1,y_temp1);
    mpf_sqrt(y_temp1,y_temp1);
    mpf_sqrt(y_temp1,y_temp1);
    mpf_ui_sub(y_temp1,1,y_temp1);
}


/*
    a_next = (a_curr * pow((1+y_next),4))-(pow(2,((2*count)+3)*y_next*(1+y_next+pow(y_next,2)));
*/
void *start_3()
{
    //a_temp = y_next*(1+y_next+pow(y_next,2));
    mpf_pow_ui(a_temp,y_next,2);
    mpf_add(a_temp,y_next,a_temp);
    mpf_add_ui(a_temp,a_temp, 1);
    mpf_mul(a_temp,y_next,a_temp);
}
void *start_4()
{
    //temp = pow(2,((2*count)+3);
    temp = 2*count+3;
    temp = pow(2,temp);
}
void *start_5()
{
    //a_temp1 = a_curr * pow((1+y_next),4);
    mpf_add_ui(a_temp1,y_next,1);
    mpf_pow_ui(a_temp1,a_temp1,4);
    mpf_mul(a_temp1,a_curr,a_temp1);
}


int main()
{
    /*
	Variáveis da biblioteca time.h, para medir o tempo de execução do programa.
    */	
    time_t begin, end;
    double dif_time;

    //Pega o tempo de iníico
    time(&begin);

    /* 10 000 000 * log2(10)
        Precisao minima para calcular a precisao de 10 milhoes de digitos
    */
    mpf_set_default_prec(33219280);
   
    /*Inicializacao de Variaveis */
    mpf_init(y_curr);
    mpf_init(y_next);
    mpf_init(a_curr);
    mpf_init(a_next);
    mpf_init(y_temp);
    mpf_init(y_temp1);
    mpf_init(a_temp);
    mpf_init(a_temp1);
    mpf_init(pi);

    /*
        y_0 = ((sqrt(2))-1)
    */
    mpf_sqrt_ui(y_curr,2);
    mpf_sub_ui(y_curr,y_curr,1);

    /*
        a_0 = (6-(4*sqrt(2)))
    */
    mpf_sqrt_ui(a_curr,2);
    mpf_mul_ui(a_curr, a_curr,4);
    mpf_ui_sub(a_curr,6,a_curr);


    for(count=0;count<12;count++)
    {
        create_threads();                       //cria as threads.
    }
    time(&end);
    // Calcula a diferença entre o tempo inicial e final
    dif_time=difftime (end,begin);
 
    printf("Quantidade iteracoes: %.d\n\n",count);
    printf("Tempo total: %lf segundos.\n\n", dif_time);
    gmp_printf("pi = %.10000000Ff\n\n\n",pi);
    
    /* Libera memoria utilizada */
    mpf_clears(y_curr,y_next,a_curr,a_next,y_temp,y_temp1,a_temp,a_temp1,pi,NULL);
return 0;
}


